package com.varian.security.config;

import cn.hutool.core.util.StrUtil;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.HashMap;
import java.util.Map;

/**
 * @author ben
 * @since 2024/6/28
 */
public class DelegatingPasswordEncoder implements PasswordEncoder {

    private final String idPrefix = "{";

    private final String idSuffix = "}";

    private final String defaultIdForEncode;

    private final Map<String, PasswordEncoder> idToPasswordEncoder;

    public DelegatingPasswordEncoder(String defaultIdForEncode, Map<String, PasswordEncoder> idToPasswordEncoder) {
        this.defaultIdForEncode = defaultIdForEncode;
        this.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);
    }

    @Override
    public String encode(CharSequence prefixRawPassword) {
        if (StrUtil.isEmpty(prefixRawPassword)) {
            return null;
        }
        String idForEncode = extractId((String) prefixRawPassword);
        String rawPassword = extractEncodedPassword((String) prefixRawPassword);
        return this.idPrefix + idForEncode + this.idSuffix + this.idToPasswordEncoder.get(idForEncode).encode(rawPassword);
    }

    @Override
    public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
        if (rawPassword == null && prefixEncodedPassword == null) {
            return true;
        }
        String idForEncode = extractId(prefixEncodedPassword);
        String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
        return this.idToPasswordEncoder.get(idForEncode).matches(rawPassword, encodedPassword);
    }

    private String extractEncodedPassword(String prefixEncodedPassword) {
        int start = prefixEncodedPassword.indexOf(this.idSuffix);
        return prefixEncodedPassword.substring(start + this.idSuffix.length());
    }

    private String extractId(String prefixEncodedPassword) {
        int start = prefixEncodedPassword.indexOf(this.idPrefix);
        if (start != 0) {
            return defaultIdForEncode;
        }
        int end = prefixEncodedPassword.indexOf(this.idSuffix, start);
        if (end < 0) {
            return defaultIdForEncode;
        }
        return StrUtil.blankToDefault(StrUtil.sub(prefixEncodedPassword, start + this.idPrefix.length(), end),
                defaultIdForEncode);
    }

    @Override
    public boolean upgradeEncoding(String prefixEncodedPassword) {
        String id = extractId(prefixEncodedPassword);
        String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
        return this.idToPasswordEncoder.get(id).upgradeEncoding(encodedPassword);
    }

}
